// @ts-check
import * as THREE from 'three'

class Player {
  constructor(params, cb) {
    let defaultParams = {
      url: '/marine_anims_core.json',
      idleWeight: 0.0,
      walkWeight: 1.0,
      runWeight: 0.0,
    }
    this.params = {
      ...defaultParams,
      ...params
    }
    this.clock = new THREE.Clock()
    const loader = new THREE.ObjectLoader()
    let mesh
    loader.load(this.params.url, (obj) => {
      obj.traverse(child => {
        if ( child instanceof THREE.SkinnedMesh ) {
          mesh = child
        }
      })
      if (mesh === undefined) return console.error('no model')

      this.playerMesh = mesh
      this.skeleton = new THREE.SkeletonHelper(mesh)
      this.skeleton.visiable = params.skeletonVisiable || false
      let mixer = new THREE.AnimationMixer(mesh)

      // fitting 装载
      this.idleAction = mixer.clipAction('idle')
      this.walkAction = mixer.clipAction('walk')
      this.runAction = mixer.clipAction('run')
      this.currentAction = this.idleAction
      this.targetAction = this.idleAction
      this.mixer = mixer
      this.actions = [this.idleAction, this.walkAction, this.runAction]
      this.mesh = mesh

      this.setWeight(this.walkAction, this.params.walkWeight)
      cb(mesh)
    })
  }

  setWeight = (action, weight) => {
    action.enabled = true
    action.setEffectiveTimeScale(1)
    action.setEffectiveWeight(weight)
  }

  prepareCrossFade = ( startAction, endAction, defaultDuration ) => {
    const duration = this.setCrossFadeDuration( defaultDuration )
    this.singleStepMode = false
    this.unPauseAllActions()

    if ( startAction === this.idleAction ) {
      this.executeCrossFade( startAction, endAction, duration )
    } else {
      this.synchronizeCrossFade( startAction, endAction, duration )
    }

  }

  unPauseAllActions = () => {
    this.actions.forEach( function ( action ) {
      action.paused = false
    } )
  }

  executeCrossFade = (startAction, endAction, duration) => {
    // setWeight( endAction, 1 )
    endAction.time = 0
    startAction.crossFadeTo( endAction, duration, true )
  }

  setCrossFadeDuration = ( defaultDuration ) => {
    if ( this.params[ 'use default duration' ] ) {
      return defaultDuration
    } else {
      return this.params[ 'set custom duration' ]
    }
  }

  synchronizeCrossFade = ( startAction, endAction, duration ) => {
    this.mixer.addEventListener( 'loop', onLoopFinished )
    function onLoopFinished( event ) {
      if ( event.action === startAction ) {
        this.mixer.removeEventListener( 'loop', onLoopFinished );
        this.executeCrossFade( startAction, endAction, duration );
      }
    }
  }

  walk = () => {
    this.actions.forEach(action => {
      action.play()
    })
    this.targetAction = this.walkAction
  }

  run = () => {
    this.actions.forEach(action => {
      action.play()
    })
    this.targetAction = this.runAction
  }

  idle = () => {
    (this.actions || []).forEach(action => {
      action.stop()
    })
    this.targetAction = this.idleAction
  }

  exec = () => {
    this.params.idleWeight = this.idleAction.getEffectiveWeight()
    this.params.walkWeight = this.walkAction.getEffectiveWeight()
    this.params.runWeight = this.runAction.getEffectiveWeight()
    
    
    this.prepareCrossFade(this.currentAction, this.targetAction, 1)
    let mixerUpdateDelta = this.clock.getDelta()
    this.mixer.update( mixerUpdateDelta )
  }
}

export default Player
